#pragma once

#include <iostream>
#include <string>
#include <poll.h>
#include "Log.hpp"
#include "Socket.hpp"

using namespace Net_Work;

const static int gdefaultport = 8888;
const static int gbacklog = 8;
const int gnum = 1024;

class PollServer
{
private:
    void HandlerEvent()
    {
        for (int i = 0; i < _num; i++)
        {
            if (_rfds[i].fd == -1)
                continue;

            // 合法的fd
            // 读事件分两种，一类是新链接的到来，一类是新数据的到来
            int fd = _rfds[i].fd;
            short revent = _rfds[i].revents;
            
            if (revent & POLLIN)
            {
                // 读事件就绪 -> 新链接的到来
                if (fd == _listensock->GetSocket())
                {
                    lg.LogMessage(Info, "get a new link\n");
                    std::string clientip;
                    uint16_t clientport;
                    // 这里不会阻塞，因为select已经检测到listensock就绪了
                    int sock = _listensock->AcceptConnection(&clientip, &clientport);
                    if (!sock)
                    {
                        lg.LogMessage(Error, "accept error\n");
                        continue;
                    }
                    lg.LogMessage(Info, "get a client, client info is# %s:%d, fd:%d\n", clientip.c_str(), clientport, sock);
                    // 获取成功了，但是我们不能直接读写，底层的数据不确定是否就绪
                    // 新链接fd到来时，要把新链接fd交给select托管 --- 只需要添加到数组_rfds_array中即可
                    int pos = 0;
                    for (; pos < _num; pos++)
                    {
                        if (_rfds[pos].fd == -1)
                        {
                            _rfds[pos].fd = sock;
                            break;
                        }
                    }
                    if (pos == _num)
                    {
                        // 1. 扩容
                        // 2. 关闭
                        close(sock);
                        lg.LogMessage(Warning, "server is full ... !\n");
                    }
                }
                // 新数据的到来
                else
                {
                    char buffer[1024];
                    ssize_t n = recv(fd, buffer, sizeof(buffer-1), 0);
                    if(n)
                    {
                        buffer[n] = 0;
                        lg.LogMessage(Info, "client say# %s\n", buffer);
                        std::string message = "你好，";
                        message += buffer;
                        send(fd, message.c_str(), message.size(), 0);
                    }
                    else
                    {
                        lg.LogMessage(Warning, "client quit, maybe close or error, close fd: %d\n", fd);
                        close(fd);
                        // 取消对poll的关心
                        _rfds[i].fd = -1;
                        _rfds[i].events = 0;
                        _rfds[i].revents = 0;
                    }
                }
            }
        }
    }

public:
    PollServer(int port = gdefaultport)
        : _port(port)
        , _listensock(new TcpSocket())
        ,_isrunning(false)
        ,_num(gnum)
    {
    }

    void InitServer()
    {
        _listensock->BuildListenSocketMethod(_port, gbacklog);
        _rfds = new struct pollfd[_num];
        for(int i = 0; i < _num; i++)
        {
            _rfds[i].fd = -1;
            _rfds[i].events = 0;
            _rfds[i].revents = 0;
        } 
        // 刚开始时，只有一个文件描述符listensock
        _rfds[0].fd = _listensock->GetSocket();
        _rfds[0].events |= POLLIN;
    }

    void Loop()
    {
        _isrunning = true;

        while (_isrunning)
        {
            // 定义时间
            int timeout = 1000;
            // rfds本质是一个输入输出型参数，rfds是在select调用返回的时候，不断被修改，所以每次都要重置
            // PrintDebug();
            int n = poll(_rfds, _num, timeout);
            switch (n)
            {
            case 0:
                lg.LogMessage(Info, "poll timeout ... \n");
                break;
            case -1:
                lg.LogMessage(Error, "poll error !!! \n");
            default:
                lg.LogMessage(Info, "poll success, begin event handler\n");
                HandlerEvent();
                break;
            }
        }

        _isrunning = false;
    }

    void stop()
    {
        _isrunning = false;
    }

    void PrintDebug()
    {
        // std::cout << "current select rfds list is: ";
        // for (int i = 0; i < num; i++)
        // {
        //     if (_rfds_array[i] == nullptr)
        //         continue;
        //     else
        //         std::cout << _rfds_array[i]->GetSocket() << " ";
        // }
        // std::cout << std::endl;
    }

    ~PollServer()
    {
        delete []_rfds;
    }

private:
    std::unique_ptr<Socket> _listensock;
    int _port;
    bool _isrunning;
    struct pollfd *_rfds;
    int _num;
};
